package com.zj.jdbc3Batis.tools.proxy;

import com.zj.jdbc3Batis.tools.annotations.Select;
import com.zj.jdbc3Batis.tools.annotations.Update;
import com.zj.jdbc3Batis.tools.BeanListHandler;
import com.zj.jdbc3Batis.tools.JdbcExecutor;
import com.zj.jdbc3Batis.tools.JdbcThreadLocal;
import com.zj.util.RegexUtil;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;

public class MapperProxy implements InvocationHandler{
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String sql="";
        String sqlChange="";
        List<String> paramStr=new ArrayList<>();
        boolean updateNeed=findMethodStartWith(method.getName());
        if(updateNeed){
            Update update = method.getAnnotation(Update.class);
            if(update!=null){
                sql=update.value();
            }
        }else{
            Select select = method.getAnnotation(Select.class);
            if(select!=null){
                sql=select.value();
            }
        }

        sqlChange= RegexUtil.getSqlAndParamInSQL(sql,paramStr);

        /**
         * 1,取出method方法上注解的sql
         * 2、将sql中的占位符中的变量取出及将sql的占位符改为？
         * 3、取出method的形参变量，将占位符变量和形参变量做匹配
         * 若有单个参数：
         *      若为基本类型，则只能有1个占位符并对应。
         *      若为map，则将占位符中的变量在map中取值并对应。
         *      若为bean，则将占位符中的变量拆分为变量和属性，并对应。注意多层级的属性。
         * 若有多个参数：则用0-9标示变量或注解标示
         *      具体对应规则如上。
         * 4、获取method的结果类型，若为基本类型，map类型，bean类型
         */
        String[] methodParamNameSimple=new String[method.getParameters().length] ;
        for (int i=0;i<method.getParameterCount();i++) {
            //这里的getName取出的是arg0，arg1等，截取0，1，2等作为参数名。
            methodParamNameSimple[i]=method.getParameters()[i].getName().substring(3);
        }
        Object[] params=new Object[paramStr.size()];
        for(int i=0;i<paramStr.size();i++){
            String[] splitParams=paramStr.get(i).split("\\.");//取出参数绑定前缀来判断是入参的第几个参数。
            int index=sqlParamPrefixInArray(splitParams[0],methodParamNameSimple);
            if(index>=0){
                //如果是简单类型，即sql中的参数名就用的0，1，2等。
                if(paramStr.get(i).equals(methodParamNameSimple[index])){
                    params[i]=args[index];
                }else {
                //如果是对象类型的属性，即sql中参数名用的0.xxx等
                    Field field=args[index].getClass().getDeclaredField(splitParams[1]);
                    field.setAccessible(true); // 私有属性必须设置访问权限
                    params[i] = field.get(args[index]);
                }
            }else{
                throw new Exception("sql绑定参数匹配错误");
            }
        }
        if(updateNeed){
            return JdbcExecutor.update(JdbcThreadLocal.getConnection(),sqlChange,params);
        }
        System.out.println("==============根据查询结果类型===================");
        Type returnType = method.getGenericReturnType();
        if(returnType instanceof ParameterizedType){
            Type[] typeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
            return JdbcExecutor.query(JdbcThreadLocal.getConnection(),sqlChange,params,new BeanListHandler((Class)typeArguments[0]));
        }else {
            return JdbcExecutor.query(JdbcThreadLocal.getConnection(),sqlChange,params,new BeanListHandler(returnType.getClass()));
        }
    }

    private boolean findMethodStartWith(String name){
        if(name.startsWith("insert")||name.startsWith("add")||name.startsWith("create")){
            return  true;
        }
        if(name.startsWith("delete")){
            return true;
        }
        if(name.startsWith("update")||name.startsWith("change")){
            return true;
        }
        return  false;
    }

    public int sqlParamPrefixInArray(String prefix,String[] args){
        for(int i=0;i<args.length;i++){
            if(prefix.startsWith(args[i])){
                return  i;
            }
        }
        return  -1;
    }
}
